/*
*	Copyright (C) 2014  Julio Montes, Mario Carrillo
*
*	Julio Montes e-mail: imc.coder@gmail.com
*
*	Mario Carrillo e-mail: mario_90ago@live.com.mx
*
*	BSD 3-Clause License
*/

#include "include/iaReplaceDialog.h"

#include "include/iaManager.h"
#include "sdk/include/editormgr.h"
#include "sdk/include/editor.h"


BEGIN_EVENT_TABLE( iaReplaceDialog, iaFindDialogBase )
    EVT_BUTTON( wxID_REPLACE, iaReplaceDialog::OnReplace )
    EVT_SHOW( iaReplaceDialog::OnShow )
    EVT_TEXT_ENTER( iaID_FDBASE_CBFIND, iaReplaceDialog::OnReplace )
    EVT_TEXT_ENTER( iaID_RD_CBREPLACE, iaReplaceDialog::OnReplace )
END_EVENT_TABLE()

iaReplaceDialog::iaReplaceDialog( wxWindow *parent )
    : iaFindDialogBase( parent, wxID_ANY, wxT( "Replace" ), wxDefaultPosition, wxSize( 400, 460 ) )
{
    stReplace = new wxStaticText( this, wxID_ANY, wxT( "Replace with: " ) );
    cbReplace = new wxComboBox( this, iaID_RD_CBREPLACE, wxT( "" ), wxDefaultPosition, wxDefaultSize, 0, NULL, wxTE_PROCESS_ENTER );

    bsInputText->Add( stReplace, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 20 );
    bsInputText->AddSpacer( 5 );
    bsInputText->Add( cbReplace, 0, wxEXPAND | wxLEFT | wxRIGHT, 20 );

    wxBoxSizer *bsButtons = new wxBoxSizer( wxHORIZONTAL );

    wxButton *btnFindReplace = new wxButton( this, wxID_REPLACE );
    wxButton *btnCancel = new wxButton( this, wxID_CANCEL );
    bsButtons->Add( btnFindReplace, 0, wxRIGHT, 5 );
    bsButtons->Add( btnCancel, 0, wxLEFT, 5 );

    bsMain->Add( bsButtons, 0, wxALIGN_CENTRE | wxTOP | wxBOTTOM, 20 );
}

iaReplaceDialog::~iaReplaceDialog()
{

}


void iaReplaceDialog::OnReplace( wxCommandEvent &evt )
{
    if( cbFind->GetValue() != wxEmptyString )
    {
        EndModal( wxID_REPLACE );
        sdk::Editor *se = sdk::EditorManager::Get()->GetCurrentEditor();

        const wxString &fndString( cbFind->GetValue() );
        const wxString &rmplString( cbReplace->GetValue() );

        if( cbFind->FindString( fndString, true ) == wxNOT_FOUND )
            cbFind->Append( fndString );
        if( cbReplace->FindString( rmplString, true ) == wxNOT_FOUND )
            cbReplace->Append( rmplString );

        flags = 0;
        flags |= cbMatchCase->IsChecked() ? wxSCI_FIND_MATCHCASE : 0;
        flags |= cbRegExp->IsChecked() ? wxSCI_FIND_REGEXP : 0;
        flags |= cbPosixRegExp->IsChecked() ? wxSCI_FIND_POSIX : 0;
        flags |= cbWholeWord->IsChecked() ? wxSCI_FIND_WHOLEWORD : 0;
        flags |= cbStartWord->IsChecked() ? wxSCI_FIND_WORDSTART : 0;

        const long currentPos( se->GetCurrentPos() );
        const long startWord( se->WordStartPosition( currentPos, true ) );
        const long endWord( se->WordEndPosition( currentPos, true ) );
        const long lenText( se->GetLength() );

        const bool directionUp( !cbRegExp->IsChecked() && rbUp->GetValue() );

        long org_startFind;
        long startFind;
        long endFind;
        long restartFind;

        if( rbStartFile->GetValue() )
        {
            startFind = directionUp ? lenText : 0;
            org_startFind = startFind;
            endFind = directionUp ? 0 : lenText;
            restartFind = directionUp ? startFind : 0;
        }
        else
        {
            if( rbGlobal->GetValue() )
            {
                startFind = directionUp ? startWord : ( endWord > currentPos ? startWord : endWord );
                org_startFind = startFind;
                endFind = directionUp ? 0 : lenText;
                restartFind = directionUp ? lenText : 0;
            }
            else
            {
                startFind = directionUp ? se->GetSelectionEnd() : se->GetSelectionStart();
                org_startFind = startFind;
                endFind = directionUp ? se->GetSelectionStart() : se->GetSelectionEnd();
                restartFind = startFind;
            }
        }

        bool stop = false;
        int countMatches = 0;
        int countReplace = 0;
        bool firstFind = true;
        const int incr = fndString.Len() <= rmplString.Len() ? rmplString.Len() - fndString.Len() : fndString.Len() - rmplString.Len();
        iaReplaceMessageBox dlg( this );
        while( !stop )
        {
            int lenFound;
            int posFound = se->FindText( startFind, endFind, fndString, flags, &lenFound );
            int countrmpl;

            if( posFound != wxSCI_INVALID_POSITION )
            {
                se->SetSelection( posFound, posFound + lenFound );
                se->EnsureCaretVisible();

                switch( dlg.ShowModal() )
                {
                    case iaID_RMB_ALL:
                        countrmpl = ReplaceWithTarget( startFind, endFind, restartFind );
                        countReplace += countrmpl;
                        countMatches += countrmpl;
                        stop = true;
                    break;

                    case iaID_RMB_CANCEL:
                        return;

                    case iaID_RMB_NO:
                        ++countMatches;
                    break;

                    case iaID_RMB_YES:
                        se->ReplaceSelection( rmplString );
                        ++countReplace;
                        ++countMatches;
                        if( !rbUp->GetValue() )
                            endFind += incr;
                    break;
                }

                startFind = ( rbUp->GetValue() ? posFound : posFound + lenFound );
            }
            else
            {
                if( cbAutoWrap->IsChecked() )
                {
                    if( !firstFind )
                        stop = true;

                    else
                    {
                        startFind = restartFind;
                        endFind = org_startFind;
                        firstFind = false;
                    }
                }
            }
        }

        if( countMatches != 0 )
            wxMessageBox( wxString::Format( wxT( "Replaced %d of %d matches" ), countReplace, countMatches ), wxT( "Replace" ) );
        else
            wxMessageBox( wxT( "Not found: " ) + fndString, wxT( "Find" ) );
    }
}

int iaReplaceDialog::ReplaceWithTarget( long start_find, long end_find, long re_start_find )
{
    const long org_start_find = start_find;
    const wxString &fndString( cbFind->GetValue() );
    const wxString &rmplString( cbReplace->GetValue() );
    sdk::Editor *se = sdk::EditorManager::Get()->GetCurrentEditor();
    const int incr = fndString.Len() <= rmplString.Len() ? rmplString.Len() - fndString.Len() : fndString.Len() - rmplString.Len();

    bool firstFind = true;
    int countReplace = 0;

    while( true )
    {
        int lenFound;
        int posFound = se->FindText( start_find, end_find, fndString, flags, &lenFound );

        if( posFound != wxSCI_INVALID_POSITION )
        {
            se->SetTargetStart( posFound );
            se->SetTargetEnd( posFound + lenFound );
            se->ReplaceTarget( rmplString );
            ++countReplace;

            if( !rbUp->GetValue() )
                end_find += incr;

            start_find = ( rbUp->GetValue() ? posFound : posFound + lenFound );
        }
        else
        {
            if( cbAutoWrap->IsChecked() )
            {
                if( !firstFind )
                    return countReplace;
                else
                {
                    start_find = re_start_find;
                    end_find = org_start_find;
                    firstFind = false;
                }
            }
            else
                return countReplace;
        }
    }
}





void iaReplaceDialog::OnShow( wxShowEvent& evt )
{
    sdk::Editor *se = sdk::EditorManager::Get()->GetCurrentEditor();

    if( evt.IsShown() )
    {
        if( se != NULL )
        {
            this->Enable( true );
            const wxString &selText( se->GetSelectedText() );
            cbReplace->SetValue( wxT( "" ) );
            if( selText.Find( wxT( '\n' ) ) != wxNOT_FOUND )
            {
                cbFind->SetValue( wxT( "" ) );
                rbSelectText->SetValue( true );
            }
            else
            {
                cbFind->SetValue( selText );
                rbGlobal->SetValue( true );
            }

            cbFind->SetFocus();
        }
        else
            this->Enable( false );
    }

    evt.Skip();
}









BEGIN_EVENT_TABLE( iaReplaceMessageBox, wxDialog )
    EVT_BUTTON( wxID_ANY, iaReplaceMessageBox::OnButton )
END_EVENT_TABLE()


iaReplaceMessageBox::iaReplaceMessageBox( wxWindow *parent )
    : wxDialog( parent, wxID_ANY, wxT( "Confirm replace" ), wxDefaultPosition, wxSize( 400 ,100 ) )
{
    wxBoxSizer *bsMain = new wxBoxSizer( wxVERTICAL );
    wxStaticText *st = new wxStaticText(  this, wxID_ANY, wxT( "Replace this occurrence?" ) );
    bsMain->Add( st, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 15 );

    wxBoxSizer *bsHorizontal = new wxBoxSizer( wxHORIZONTAL );

    wxButton *btnYes = new wxButton( this, iaID_RMB_YES, wxT( "Yes" ) );
    btnYes->SetFocus();
    wxButton *btnNo = new wxButton( this, iaID_RMB_NO, wxT( "No" ) );
    wxButton *btnAll = new wxButton( this, iaID_RMB_ALL, wxT( "All" ) );
    wxButton *btnCancel = new wxButton( this, iaID_RMB_CANCEL, wxT( "Cancel" ) );

    bsHorizontal->Add( btnYes, 0 );
    bsHorizontal->Add( btnNo, 0 );
    bsHorizontal->Add( btnAll, 0 );
    bsHorizontal->Add( btnCancel, 0 );
    bsMain->Add( bsHorizontal, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10  );

    SetSizer( bsMain );
}


iaReplaceMessageBox::~iaReplaceMessageBox()
{

}


void iaReplaceMessageBox::OnButton( wxCommandEvent &evt )
{
    EndModal( evt.GetId() );
}


